package service

import (
	"encoding/json"
	"github.com/gomodule/redigo/redis"
	"github.com/pkg/errors"
	"github.com/spf13/cast"
	"nft-server/common"
	"nft-server/db"
	"nft-server/logger"
	"nft-server/util"
	"time"
)

const (
	MintTaskQueryTimeIntervalSecond = 5
	RedisMintTask                   = "redisMintTask"
)

func MintTaskInit() {
	go func() {
		logger.Info("Mint queue Init successfully")
		for {
			mintTask, err := GetMintTaskFromRedis(db.RedisPool)
			if err != nil {
				logger.Error("MintTask error: %v", err)
				time.Sleep(MintTaskQueryTimeIntervalSecond * time.Second)
				continue
			}
			if mintTask == nil {
				time.Sleep(MintTaskQueryTimeIntervalSecond * time.Second)
				continue
			}
			//从数据库中取出铸造任务记录
			err = MintTask(mintTask)
			if err != nil {
				logger.Error("Mint error: %v", err)
				time.Sleep(MintTaskQueryTimeIntervalSecond * time.Second)
				continue
			}
		}
	}()
}
func MintTask(req *common.MintMultipleReq) error {
	if len(req.MintData) < 1 {
		return errors.Errorf("mint task size < 1")
	}
	collection, err := GetCollectionDetailByUserIdAndCollection(req.UserId, req.MintData[0].CollectionIndex, util.DefaultSubCollectionIndex)
	collection.CollectionStatus = util.CollectionMintInProgress //设置集合状态为铸造中
	err = UpdateCollectionDetail(collection)
	//startTokenID获取--暂时直接从collection_manager表中的supplies
	startTokenID, err := GetNowTokenId(req.UserId, collection.CollectionIndex, util.DefaultSubCollectionIndex)
	if err != nil {
		logger.Error("GetNowTokenId error: %v", err)
		return err
	}
	//铸造增加--同时铸造到同一个集合中
	//铸造增加--铸造到不同的子集合中，总集合库存增加
	subCollectionIndex := collection.SubCollectionIndex + 1
	for _, mintData := range req.MintData {
		err = MintOneSubCollection(mintData, subCollectionIndex, startTokenID, *req, collection)
		//更新子集合ID
		subCollectionIndex += 1
		//更新起始tokenId
		startTokenID += mintData.MintAmount
		if err != nil {
			logger.Error(err.Error())
			continue
		}
	}
	//设置总集合状态为铸造完成
	updateStatus := util.CollectionMintCompleted
	_ = UpdateCollectionStatusAndTokenId(req.UserId, collection.CollectionIndex, util.QueryOfSubCollectionIndex, &updateStatus, &startTokenID)
	//增加判断，如果是第一次铸造，则写入数据库，管理员地址
	err = CheckAdminFirstMint(collection.CollectionIndex, util.DefaultTokenId)
	if err != nil {
		logger.Error(err.Error())
		return err
	}
	return nil
}

func BuildMintTaskKeyForRedis() (string, error) {
	var partString []string
	partString = append(partString, cast.ToString(RedisMintTask))
	key := util.CombineStringWithConnect(partString)
	return key, nil
}
func BuildMintTaskValueForRedis(req common.MintMultipleReq) (string, error) {
	//通过序列化建立redis存储条件
	data, err := json.Marshal(req)
	if err != nil {
		return "", err
	}
	return cast.ToString(data), nil
}
func WriteMintTaskIntoRedis(req common.MintMultipleReq, redisPool *redis.Pool) error {
	if redisPool == nil {
		return errors.Errorf("无法连接redis")
	}
	//将该订单信息手上写入redis
	conn := redisPool.Get()
	defer conn.Close()
	//该订单对应的key
	keyOfMintTask, err := BuildMintTaskKeyForRedis()
	if err != nil {
		logger.Error(err.Error())
		return err
	}
	valueOfMintTask, err := BuildMintTaskValueForRedis(req)
	if err != nil {
		logger.Error(err.Error())
		return err
	}
	_, err = conn.Do("LPUSH", keyOfMintTask, valueOfMintTask)
	if err != nil {
		logger.Error("can't write into redis error: %v", err)
		return err
	}
	return nil
}
func GetMintTaskFromRedis(redisPool *redis.Pool) (*common.MintMultipleReq, error) {
	if redisPool == nil {
		return nil, errors.Errorf("can't connect redis")
	}
	conn := redisPool.Get()
	defer conn.Close()
	//该订单对应的key
	keyOfMintTask, err := BuildMintTaskKeyForRedis()
	responseOfRedisOnMintTask, err := redis.Strings(conn.Do("BRPOP", keyOfMintTask, 0))
	valueOfMintTask := responseOfRedisOnMintTask[1]
	if err != nil {
		logger.Error(err.Error())
		return nil, errors.Errorf("can't found in redis")
	}
	if err == nil && len(valueOfMintTask) < 1 {
		return nil, errors.Errorf("铸造任务已完成或已关闭")
	}
	mintTask := new(common.MintMultipleReq)
	err = json.Unmarshal([]byte(cast.ToString(valueOfMintTask)), mintTask)
	if err != nil {
		return nil, err
	}
	return mintTask, nil
}
